package com.snifferx.services.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

import org.primefaces.component.inputtext.InputText;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.snifferx.dao.AuthorityDao;
import com.snifferx.dao.UserDao;
import com.snifferx.domain.AuthorityEntity;
import com.snifferx.domain.UserEntity;
import com.snifferx.services.UserService;

/**
 * Service providing service methods to work with user data and entity.
 * 
 * @author Wei Cheng
 */

@Service("userService")
public class UserServiceImpl implements UserService, UserDetailsService {

	private UserDao userDao;
	private AuthorityDao authorityDao;

	/**
	 * Create user - persist to database
	 * 
	 * @param userEntity
	 * @return true if success
	 */
	public boolean createUser(UserEntity userEntity) {

		if (!userDao.checkAvailable(userEntity.getUsername())) {
			FacesMessage message = constructErrorMessage(String.format(getMessageBundle().getString("userExistsMsg"), userEntity.getUsername()), null);
			getFacesContext().addMessage(null, message);

			return false;
		}

		try {
			userDao.save(userEntity);
		} catch (Exception e) {
			FacesMessage message = constructFatalMessage(e.getMessage(), null);
			getFacesContext().addMessage(null, message);

			return false;
		}

		return true;
	}

	/**
	 * Check user name availability. UI ajax use.
	 * 
	 * @param ajax
	 *            event
	 * @return
	 */
	public boolean checkAvailable(AjaxBehaviorEvent event) {

		InputText inputText = (InputText) event.getSource();
		String value = (String) inputText.getValue();

		boolean available = userDao.checkAvailable(value);

		if (!available) {
			FacesMessage message = constructErrorMessage(null, String.format(getMessageBundle().getString("userExistsMsg"), value));
			getFacesContext().addMessage(event.getComponent().getClientId(), message);
		} else {
			FacesMessage message = constructInfoMessage(null, String.format(getMessageBundle().getString("userAvailableMsg"), value));
			getFacesContext().addMessage(event.getComponent().getClientId(), message);
		}

		return available;
	}

	/**
	 * Construct UserDetails instance required by spring security
	 */
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
		UserEntity user = userDao.loadUserByUserName(userName);
		if (user == null) {
			throw new UsernameNotFoundException(String.format(getMessageBundle().getString("badCredentials"), userName));
			// throw new
			// UsernameNotFoundException(String.format("No find this Account",
			// userName));
		}

		// will added here to check the login user role
		// AuthorityEntity authority =
		// authorityDao.loadAuthorityByUserName(userName);
		List<AuthorityEntity> authorityEntitys = user.getAuthorities();
		Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		// authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
		// authorities.add(new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
		// authorities.add(new
		// SimpleGrantedAuthority(authority.getAuthority()));

		for (int i = 0; i < authorityEntitys.size(); i++) {
			authorities.add(new SimpleGrantedAuthority(authorityEntitys.get(i).getAuthority()));
		}

		User userDetails = new User(user.getUsername(), user.getPassword(), (user.getEnabled().equals("0") ? false : true), true, true, true,
				authorities);
		return userDetails;
	}

	/**
	 * Retrieves full User record from database by user name
	 * 
	 * @param userName
	 * @return UserEntity
	 */
	public UserEntity loadUserEntityByUsername(String userName) {
		return userDao.loadUserByUserName(userName);
	}

	protected FacesMessage constructErrorMessage(String message, String detail) {
		return new FacesMessage(FacesMessage.SEVERITY_ERROR, message, detail);
	}

	protected FacesMessage constructInfoMessage(String message, String detail) {
		return new FacesMessage(FacesMessage.SEVERITY_INFO, message, detail);
	}

	protected FacesMessage constructFatalMessage(String message, String detail) {
		return new FacesMessage(FacesMessage.SEVERITY_FATAL, message, detail);
	}

	protected FacesContext getFacesContext() {
		return FacesContext.getCurrentInstance();
	}

	protected ResourceBundle getMessageBundle() {
		return ResourceBundle.getBundle("message");
	}

	public UserDao getUserDao() {
		return userDao;
	}

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public AuthorityDao getAuthorityDao() {
		return authorityDao;
	}

	public void setAuthorityDao(AuthorityDao authorityDao) {
		this.authorityDao = authorityDao;
	}

	@Override
	public boolean updateUserEntity(UserEntity userEntity) {
		try {
			userDao.update(userEntity);
		} catch (Exception e) {
			e.printStackTrace();
			
			FacesMessage message = constructFatalMessage(e.getMessage(), null);
			getFacesContext().addMessage(null, message);

			return false;
		}
		return true;
	}

}
